Skip to content

[EXPERIMENT] System.Text.Json: Performance optimizations for hot paths#2

Open
artl93 wants to merge 1 commit intomainfrom
perf/system-text-json-optimizations
Open

[EXPERIMENT] System.Text.Json: Performance optimizations for hot paths#2
artl93 wants to merge 1 commit intomainfrom
perf/system-text-json-optimizations

Conversation

@artl93
Copy link
Owner

@artl93 artl93 commented Feb 14, 2026

⚠️ Experimental PR — Do Not Merge

This is an experimental performance exploration PR. The changes here are speculative optimizations generated from a comprehensive AI-assisted performance audit and need to be validated with benchmarks before being considered for any real PR.

Changes

  1. Reader: Inline delimiter/escape checks — Replace Delimiters.Contains() (8-byte span scan) with IsDelimiter() switch expression in 5 hot-path call sites across Utf8JsonReader and Utf8JsonReader.MultiSegment. Same for EscapableChars.IndexOf()IsEscapableChar() (3 call sites).

  2. Writer: AggressiveInlining — Add [MethodImpl(MethodImplOptions.AggressiveInlining)] to ValidateWritingValue() and SetFlagToAddListSeparatorBeforeNextItem(), called on every write operation.

  3. Enum converter: Regex → manual char scan — Replace JsonHelpers.IntegerRegex.IsMatch() with a simple IsIntegerLike() loop in EnumConverter, avoiding regex engine overhead on enum deserialization cache misses.

Validation

  • All 49,965 System.Text.Json tests pass (0 failures)
  • Awaiting EgorBot benchmark results

Questions for reviewers

  • Do the IsDelimiter/IsEscapableChar changes actually outperform the JIT-optimized span search? (Benchmark pending)
  • Is the AggressiveInlining justified or does the JIT already inline these?
  • Is the regex replacement worth the maintenance cost for a cold-path optimization?

Full audit report: https://gist.github.com/artl93/8f375461b1a29d48b26053b88db76058

- Replace IntegerRegex with manual char scanning in enum parsing for
  faster integer detection in TryParseEnumFromString
- Add AggressiveInlining to ValidateWritingValue and
  SetFlagToAddListSeparatorBeforeNextItem in Utf8JsonWriter (called on
  every value write)
- Replace Delimiters.Contains() linear search with inline IsDelimiter()
  switch in number parsing hot paths (Utf8JsonReader and MultiSegment)
- Replace EscapableChars.IndexOf() linear search with inline
  IsEscapableChar() switch in string escape validation

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@artl93
Copy link
Owner Author

artl93 commented Feb 14, 2026

@EgorBot -intel -arm

using BenchmarkDotNet.Attributes;
using System.Text.Json;
using System.Text.Json.Serialization;

public class StjPerfValidation
{
    private byte[] _smallJson;
    private byte[] _nestedJson;
    private JsonSerializerOptions _options;

    public enum Color { Red, Green, Blue, Yellow, Cyan, Magenta, White, Black }

    public class SmallPoco
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public bool Active { get; set; }
        public double Score { get; set; }
    }

    [GlobalSetup]
    public void Setup()
    {
        _smallJson = JsonSerializer.SerializeToUtf8Bytes(new SmallPoco { Id = 42, Name = "test", Active = true, Score = 3.14 });
        _nestedJson = JsonSerializer.SerializeToUtf8Bytes(new { a = new { b = 1, c = "hello" }, d = new[] { 1, 2, 3 }, e = true, f = 0.0 });
        _options = new JsonSerializerOptions { Converters = { new JsonStringEnumConverter() } };
    }

    [Benchmark]
    public SmallPoco Deserialize_SmallPoco() => JsonSerializer.Deserialize<SmallPoco>(_smallJson);

    [Benchmark]
    public void Reader_TokenScan()
    {
        var reader = new Utf8JsonReader(_nestedJson);
        while (reader.Read()) { }
    }

    [Benchmark]
    public byte[] Serialize_SmallPoco() => JsonSerializer.SerializeToUtf8Bytes(new SmallPoco { Id = 42, Name = "test", Active = true, Score = 3.14 });

    [Benchmark]
    public Color Deserialize_Enum() => JsonSerializer.Deserialize<Color>("\"Blue\"", _options);

    [Benchmark]
    public string Serialize_Enum() => JsonSerializer.Serialize(Color.Green, _options);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant